Source code for hysop.operator.analytic

# Copyright (c) HySoP 2011-2024
#
# This file is part of HySoP software.
# See "https://particle_methods.gricad-pages.univ-grenoble-alpes.fr/hysop-doc/"
# for further info.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


"""Initialize fields on a grid, with a user-defined function
"""
from hysop.constants import Backend, Implementation
from hysop.fields.continuous_field import Field
from hysop.tools.htypes import check_instance, first_not_None, to_tuple
from hysop.tools.decorators import debug
from hysop.topology.cartesian_descriptor import CartesianTopologyDescriptors
from hysop.core.graph.computational_node_frontend import ComputationalGraphNodeFrontend
from hysop.core.graph.node_generator import ComputationalGraphNodeGenerator
from hysop.testsenv import __HAS_OPENCL_BACKEND__


[docs] class AnalyticField(ComputationalGraphNodeGenerator): """ Applies an analytic formula, given by user, on all contained scalar fields. Formula may be given in different formats depending on the chosen implementation backend. """ @debug def __new__( cls, field, formula, variables, extra_input_kwds=None, implementation=None, base_kwds=None, **kwds, ): base_kwds = first_not_None(base_kwds, {}) return super().__new__( cls, candidate_input_tensors=None, candidate_output_tensors=None, **base_kwds, ) @debug def __init__( self, field, formula, variables, extra_input_kwds=None, implementation=None, base_kwds=None, **kwds, ): """ AnalyticField operator frontend. Apply a user-defined formula onto a field, possibly dependent on space variables and external fields/parameters. Parameters ---------- field: hysop.field.continuous_field.Field Continuous field to be modified. formula : python function or sympy expression or tupe The formula to be applied onto the scalar fields. If the formula is a tuple of the length of the number of scalar fields, fomula[component] is used instead. variables: dict Dictionary of fields as keys and topology descriptors as values. implementation: Implementation, optional, defaults to None target implementation, should be contained in available_implementations(). If None, implementation will be set to default_implementation(). extra_input_kwds: dict, optional Extra inputs that will be forwarded to the formula. Fields and Parameters are handled correctly as input requirements. Only used for Implementation.PYTHON, discarded for other implementations. base_kwds: dict, optional Base class keyword arguments. kwds: dict, optional Extra parameters passed towards operator implementation. """ check_instance(field, Field) check_instance(variables, dict, keys=Field, values=CartesianTopologyDescriptors) extra_input_kwds = first_not_None(extra_input_kwds, {}) base_kwds = first_not_None(base_kwds, {}) assert "extra_input_kwds" not in kwds assert "component" not in kwds assert "coords" not in kwds if (implementation is Implementation.PYTHON) and (extra_input_kwds is not None): candidate_input_tensors = tuple( filter(lambda f: isinstance(f, Field), extra_input_kwds.values()) ) else: extra_input_kwds = {} candidate_input_tensors = tuple() candidate_output_tensors = (field,) formula = to_tuple(formula) if len(formula) == 1: formula = formula * field.nb_components check_instance(formula, tuple, size=field.nb_components) super().__init__( candidate_input_tensors=candidate_input_tensors, candidate_output_tensors=candidate_output_tensors, **base_kwds, ) self._fields = field.fields self._formula = formula self._variables = variables self._extra_input_kwds = extra_input_kwds self._implementation = implementation self._kwds = kwds @debug def _generate(self): nodes = [] impl = self._implementation variables = self._variables assert len(self._formula) == len(self._fields) for component, (formula, field) in enumerate(zip(self._formula, self._fields)): if formula is None: continue kwds = self._kwds.copy() extra_input_kwds = self._extra_input_kwds.copy() extra_input_kwds["component"] = component node = AnalyticScalarField( field=field, formula=formula, variables=variables, implementation=impl, extra_input_kwds=extra_input_kwds, **kwds, ) nodes.append(node) return nodes
[docs] class AnalyticScalarField(ComputationalGraphNodeFrontend): """ Applies an analytic formula, given by user, on its field. Formula may be given in different formats depending on the chosen implementation backend. """
[docs] @classmethod def implementations(cls): from hysop.backend.host.python.operator.analytic import PythonAnalyticField __implementations = { Implementation.PYTHON: PythonAnalyticField, } if __HAS_OPENCL_BACKEND__: from hysop.backend.device.opencl.operator.analytic import ( OpenClAnalyticField, ) __implementations.update({Implementation.OPENCL: OpenClAnalyticField}) return __implementations
[docs] @classmethod def default_implementation(cls): return Implementation.PYTHON
@debug def __init__( self, field, formula, variables, extra_input_kwds=None, implementation=None, base_kwds=None, **kwds, ): """ AnalyticField operator frontend. Apply a user-defined formula onto a field, possibly dependent on space variables and external fields/parameters. Parameters ---------- field: hysop.field.continuous_field.Field Continuous field to be modified. formula : python function or sympy expression The formula to be applied onto the field. variables: dict Dictionary of fields as keys and topology descriptors as values. implementation: Implementation, optional, defaults to None target implementation, should be contained in available_implementations(). If None, implementation will be set to default_implementation(). extra_input_kwds: dict, optional Extra inputs that will be forwarded to the formula. Fields and Parameters are handled correctly as input requirements. Only used for Implementation.PYTHON, discarded for other implementations. base_kwds: dict, optional Base class keyword arguments. kwds: dict, optional Extra parameters passed towards operator implementation. """ check_instance(field, Field) check_instance(variables, dict, keys=Field, values=CartesianTopologyDescriptors) if implementation is Implementation.PYTHON: kwds["extra_input_kwds"] = extra_input_kwds super().__init__( field=field, formula=formula, variables=variables, implementation=implementation, base_kwds=base_kwds, **kwds, )